package com.alexeyhanin.tapestry.goodies.guice;

import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import org.apache.tapestry5.ioc.AnnotationProvider;
import org.apache.tapestry5.ioc.ObjectLocator;
import org.apache.tapestry5.ioc.ObjectProvider;
import org.apache.tapestry5.ioc.annotations.UsesConfiguration;

import java.util.Collection;
import java.util.List;

/**
 * GuiceObjectProvider allows objects bound within <a href="http://code.google.com/p/google-guice/">Guice</a>
 * module(-s) transparently availaible in Tapestry.
 * <p/>
 * To start using your guice-assembled objects just configure GuiceObjectProvider in your Tapestry module:
 * <pre>
 * public static void contributeGuiceObjectProvider({@link org.apache.tapestry5.ioc.Configuration Configuration}<{@link com.google.inject.Module Module}> configuration) {
 *   configuration.add(new MyModule());
 * }
 * <pre>
 *
 * @author aha
 * @since Sep 9, 2009
 *        $Id: GuiceObjectProvider.java 2 2009-11-26 19:22:03Z gagarin61 $
 */
@UsesConfiguration(ObjectProvider.class)
public class GuiceObjectProvider implements ObjectProvider {

    private Injector injector;

    public GuiceObjectProvider(Collection<Module> modules) {
        injector = Guice.createInjector(modules);
    }

    // for test
    GuiceObjectProvider() {
    }

    // for test
    void setInjector(Injector injector) {
        this.injector = injector;
    }

    public <T> T provide(final Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator) {
        final T result;
        final List<Binding<T>> bindings = injector.findBindingsByType(TypeLiteral.get(objectType));
        switch (bindings.size()) {
            case 0:
                result = null;
                break;
            case 1:
                result = bindings.iterator().next().getProvider().get();
                break;
            default:
                throw new IllegalStateException("More than one binding for type " + objectType + " found.");
        }
        return result;
    }
}
